/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package se.kth.lwr.jeat.core;

import java.util.ArrayList;
import java.util.List;

/**
 * This class represent a function of several {@link DataSource}es. Basicly it
 * defines a sequence of mathematical operations on an initial {@link DataSource}.
 * All arguments in these mathematical operations are also required to be
 * DataSources.
 *
 * @param <V> type of the value returned by this SourceFunction
 *
 * @author davidev
 */
public abstract class SourceFunction<V> extends DataSource<V> {

    private List<Operator<V, ?>> sequenceOfOperations;
    private DataSource<V> initialSourceValue;
    private V initialValue;

    /**
     * Creates a SourceFunction with a custom initial value
     *
     * @param name name the function
     * @param description description of the function
     * @param initialValue initial value
     */
    public SourceFunction(String name, String description, DataSource<V> initialValue) {
        super(name, description);
        sequenceOfOperations = new ArrayList<>();
        this.initialSourceValue = initialValue;
    }

    /**
     * Creates a SourceFunction with an initial value that is not wrapped in a
     * dataSource. Useful to create Functions initialized as dimensionless
     *
     * @param name name of source
     * @param description description of source
     */
    public SourceFunction(String name, String description, V initialValue) {
        super(name, description);
        sequenceOfOperations = new ArrayList<>();
        this.initialValue = initialValue;
    }

    /**
     * returns the initial value of the function. If it's a DataSource, the
     * usage is recorded
     *
     * @returns the value of the initial DataSource
     */
    protected V useBaseSource(Activity dataUser) {
        if (initialSourceValue != null) {
            return initialSourceValue.useValue(dataUser);
        } else {
            return initialValue;
        }
    }

    /**
     * Returns the value of the initial value of the function. Usage will not be
     * recored
     *
     * @return the value of the initial DataSource
     */
    protected V readBaseSource() {
        if (initialSourceValue != null) {
            return initialSourceValue.readValue();
        } else {
            return initialValue;
        }
    }

    /**
     * Record a new operation on this function
     *
     *
     * @param operator mathematical operator to be added
     */
    protected void recordOperation(Operator<V, ?> operator) {
        sequenceOfOperations.add(operator);
    }

    /**
     *
     * @return the current sequence of operations recorded on this function
     */
    protected List<Operator<V, ?>> getRecordedOperations() {
        return sequenceOfOperations;
    }

    /**
     * returns the value of this SourceFunction and record usage of all
     * DataSources used in the operator sequence.
     *
     * @param user the user of the DataSources
     * @return the value of this SourceFunction
     */
    @Override
    public V useValue(Activity user) {
        //Decided to remove this line since most source functions are not interesting 
        //to follow. However, it would be very nice if the final function that builds
        //the emissions in the activity could be viewed. This would be possible in
        //MultiplierActivities and should probably be implemented from within these activities

        //call to superclass to ensure usage registration of the function
        //super.useValue(user);

        //use and get initial source
        V retval = useBaseSource(user);
        //perform operations
        for (Operator<V, ?> op : getRecordedOperations()) {
            retval = op.operate(retval, user);
        }
        //return resulting value
        return retval;
    }

    /**
     * returns the value of this SourceFunction WITHOUT recording usage of all
     * DataSources used in the operator sequence.
     *
     * @return the value of this SourceFunction
     */
    @Override
    public V readValue() {
        V retval = readBaseSource();
        for (Operator<V, ?> op : getRecordedOperations()) {
            retval = op.operate(retval);
        }
        return retval;
    }
}
